/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.modules.system.service.impl;

import cn.hutool.core.map.MapUtil;
import me.zhengjie.constant.ConstantUtils;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.system.domain.CourseOrder;
import me.zhengjie.exception.EntityExistException;
import me.zhengjie.modules.system.domain.ExamOrder;
import me.zhengjie.modules.system.domain.WxUser;
import me.zhengjie.modules.system.service.CourseInfoService;
import me.zhengjie.modules.system.service.WxUserService;
import me.zhengjie.modules.system.service.dto.*;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.system.repository.CourseOrderRepository;
import me.zhengjie.modules.system.service.CourseOrderService;
import me.zhengjie.modules.system.service.mapstruct.CourseOrderMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.math.BigDecimal;
import java.util.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 王宇
 * @website https://el-admin.vip
 * @description 服务实现
 * @date 2023-09-16
 **/
@Service
@RequiredArgsConstructor
public class CourseOrderServiceImpl implements CourseOrderService {

    private final CourseOrderRepository courseOrderRepository;
    private final CourseOrderMapper courseOrderMapper;

    @Autowired
    private CourseInfoService courseInfoService;
    @Autowired
    private WxUserService wxUserService;


    @Override
    public Map<String, Object> queryAll(CourseOrderListQueryDTO criteria, Pageable pageable) {

        Map<String, Object> data = new HashMap<String,Object>(){{
            put("content", new ArrayList<>());
            put("totalElements", 0);
        }};
        CourseInfoQueryCriteria courseInfoQueryCriteria = new CourseInfoQueryCriteria();
        courseInfoQueryCriteria.setCourseName(criteria.getCourseName());
        courseInfoQueryCriteria.setPlatform(criteria.getCourseType());
        courseInfoQueryCriteria.setCreateTime(criteria.getCreateTime());
        Map<String, Object> map = courseInfoService.queryAll(courseInfoQueryCriteria, pageable);
        if (MapUtil.isEmpty(map) || Objects.isNull(map.get("content"))) {
            return data;
        }
        List<CourseInfoDto> content = (List<CourseInfoDto>) map.get("content");
        content.forEach(s -> {
            CourseOrderQueryCriteria orderQueryCriteria = new CourseOrderQueryCriteria();
            orderQueryCriteria.setCourseNo(s.getCourseNo());
            orderQueryCriteria.setPayStatus(ConstantUtils.Valid);
            List<CourseOrder> all = courseOrderRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, orderQueryCriteria, criteriaBuilder));
            if (CollectionUtils.isEmpty(all)) {
                return;
            }
            CourseInfoDto.CourseInfoOrderDTO courseInfoOrderDTO = new CourseInfoDto.CourseInfoOrderDTO();
            courseInfoOrderDTO.setEnrollNum(all.size());
            BigDecimal totalAmount = all.parallelStream().map(CourseOrder::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            courseInfoOrderDTO.setPayTotalAmount(totalAmount);
            s.setOrderDTO(courseInfoOrderDTO);
            List<CourseInfoDto.CourseInfoUserDTO> courseInfoUserDTOS = new ArrayList<>();

            all.forEach(u -> {
                WxUser user = wxUserService.findByOpenId(u.getOpenId());
                if (Objects.isNull(user)) {
                    return;
                }
                CourseInfoDto.CourseInfoUserDTO userDTO = new CourseInfoDto.CourseInfoUserDTO();
                userDTO.setName(user.getRealName());
                userDTO.setMobile(user.getMobile());
                userDTO.setCity(user.getCity());
                userDTO.setHeadPic(user.getHeadimgurl());
                userDTO.setNickName(user.getNickname());
                userDTO.setOrderNo(u.getOrderNo());
                userDTO.setPayAmount(u.getPayAmount());
                userDTO.setOrderStatus(u.getPayStatus());
                courseInfoUserDTOS.add(userDTO);
            });
            courseInfoOrderDTO.setUserList(courseInfoUserDTOS);
        });
        return map;
    }

    @Override
    public List<CourseApplicantDTO> queryApplicant(String courseNo) {
        CourseOrderQueryCriteria criteria = new CourseOrderQueryCriteria();
        criteria.setCourseNo(courseNo);
        criteria.setPayStatus(ConstantUtils.Order_Payed);
        List<CourseOrderDto> courseOrderList = queryAll(criteria);
        if (CollectionUtils.isEmpty(courseOrderList)){
            return null;
        }
        List<CourseApplicantDTO> list = new ArrayList<>();
        courseOrderList.stream().forEach(s -> {
            WxUser user = wxUserService.findByOpenId(s.getOpenId());
            if (Objects.isNull(user)) {
                return;
            }
            CourseApplicantDTO userDTO = new CourseApplicantDTO();
            userDTO.setName(user.getNickname());
            userDTO.setMobile(user.getMobile());
            userDTO.setCity(user.getCity());
            userDTO.setHeadPic(user.getHeadimgurl());
            list.add(userDTO);
        });
        return list;
    }

    @Override
    public Map<String, Object> queryAll(CourseOrderQueryCriteria criteria, Pageable pageable) {
        Page<CourseOrder> page = courseOrderRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(courseOrderMapper::toDto));
    }

    @Override
    public List<CourseOrderDto> queryAll(CourseOrderQueryCriteria criteria) {
        return courseOrderMapper.toDto(courseOrderRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    public CourseOrderDto findById(Long orderId) {
        CourseOrder courseOrder = courseOrderRepository.findById(orderId).orElseGet(CourseOrder::new);
        ValidationUtil.isNull(courseOrder.getOrderId(), "CourseOrder", "orderId", orderId);
        return courseOrderMapper.toDto(courseOrder);
    }

    @Override
    public CourseOrderDto create(CourseOrder resources) {
        if (courseOrderRepository.findByOrderNo(resources.getOrderNo()) != null) {
            throw new EntityExistException(CourseOrder.class, "order_no", resources.getOrderNo());
        }
        return courseOrderMapper.toDto(courseOrderRepository.save(resources));
    }

    @Override
    public void update(CourseOrder resources) {
        CourseOrder courseOrder = courseOrderRepository.findById(resources.getOrderId()).orElseGet(CourseOrder::new);
        ValidationUtil.isNull(courseOrder.getOrderId(), "CourseOrder", "id", resources.getOrderId());
        CourseOrder courseOrder1 = null;
        courseOrder1 = courseOrderRepository.findByOrderNo(resources.getOrderNo());
        if (courseOrder1 != null && !courseOrder1.getOrderId().equals(courseOrder.getOrderId())) {
            throw new EntityExistException(CourseOrder.class, "order_no", resources.getOrderNo());
        }
        courseOrder.copy(resources);
        courseOrderRepository.save(courseOrder);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long orderId : ids) {
            courseOrderRepository.deleteById(orderId);
        }
    }

    @Override
    public void download(List<CourseOrderDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (CourseOrderDto courseOrder : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("订单编号", courseOrder.getOrderNo());
            map.put("课程编号", courseOrder.getCourseNo());
            map.put("订单金额", courseOrder.getOrderAmount());
            map.put("支付金额", courseOrder.getPayAmount());
            map.put("openid", courseOrder.getOpenId());
            map.put("预支付流水号", courseOrder.getPrePayid());
            map.put("支付流水号", courseOrder.getPaySerialid());
            map.put("支付状态，0：未支付，1：已支付", courseOrder.getPayStatus());
            map.put("订单状态", courseOrder.getOrderStatus());
            map.put("更新时间", courseOrder.getUpdateTime());
            map.put("创建时间", courseOrder.getCreateTime());
            map.put("失败原因", courseOrder.getFailMsg());
            map.put("备注", courseOrder.getRemark());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public CourseOrder findOne(String orderSn) {
        CourseOrderQueryCriteria criteria = new CourseOrderQueryCriteria();
        criteria.setOrderNo(orderSn);
        List<CourseOrder> all = courseOrderRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder));
        if (CollectionUtils.isEmpty(all)) {
            return null;
        }
        return all.get(0);
    }

    @Override
    public boolean refund(String orderSn) {
        CourseOrder courseOrder = findOne(orderSn);
        if (courseOrder == null) {
            throw new BadRequestException("订单不存在");
        }
        if (courseOrder.getPayStatus() == 0) {
            throw new BadRequestException("订单未支付，无法退款");
        }
        if (courseOrder.getPayStatus() == 2) {
            throw new BadRequestException("订单已退款");
        }
        courseOrder.setOrderStatus(2);
        courseOrder.setUpdateTime(DateUtil.TimestampNow());
        update(courseOrder);
        return true;
    }
}